# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2020 Howetuft <howetuft@gmail.com>                      *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2.1 of   *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

"""This module implements sun light physical calculations.

It mainly provides a way to compute sun power and color for Cycles sun
light, as Cycles does not provide such a feature.

Usage: one may essentially use 'sunlight' function
"""


import bisect
import numbers
from math import exp, cos, pi as PI, radians
from collections import namedtuple


# ===========================================================================
#                               Photometric data
# ===========================================================================

# CIE 1931 2° Standard Observer
# ie transcoding table from wavelength (in nanometers) to
# tristimulus color values in XYZ color space

# CIE standard observer values (tristimuli)
CIE_STD_OBSERVER_VALUES = (  # {{{1
    (0.0001299000, 0.000003917000, 0.0006061000),
    (0.0001458470, 0.000004393581, 0.0006808792),
    (0.0001638021, 0.000004929604, 0.0007651456),
    (0.0001840037, 0.000005532136, 0.0008600124),
    (0.0002066902, 0.000006208245, 0.0009665928),
    (0.0002321000, 0.000006965000, 0.001086000),
    (0.0002607280, 0.000007813219, 0.001220586),
    (0.0002930750, 0.000008767336, 0.001372729),
    (0.0003293880, 0.000009839844, 0.001543579),
    (0.0003699140, 0.00001104323, 0.001734286),
    (0.0004149000, 0.00001239000, 0.001946000),
    (0.0004641587, 0.00001388641, 0.002177777),
    (0.0005189860, 0.00001555728, 0.002435809),
    (0.0005818540, 0.00001744296, 0.002731953),
    (0.0006552347, 0.00001958375, 0.003078064),
    (0.0007416000, 0.00002202000, 0.003486000),
    (0.0008450296, 0.00002483965, 0.003975227),
    (0.0009645268, 0.00002804126, 0.004540880),
    (0.001094949, 0.00003153104, 0.005158320),
    (0.001231154, 0.00003521521, 0.005802907),
    (0.001368000, 0.00003900000, 0.006450001),
    (0.001502050, 0.00004282640, 0.007083216),
    (0.001642328, 0.00004691460, 0.007745488),
    (0.001802382, 0.00005158960, 0.008501152),
    (0.001995757, 0.00005717640, 0.009414544),
    (0.002236000, 0.00006400000, 0.01054999),
    (0.002535385, 0.00007234421, 0.01196580),
    (0.002892603, 0.00008221224, 0.01365587),
    (0.003300829, 0.00009350816, 0.01558805),
    (0.003753236, 0.0001061361, 0.01773015),
    (0.004243000, 0.0001200000, 0.02005001),
    (0.004762389, 0.0001349840, 0.02251136),
    (0.005330048, 0.0001514920, 0.02520288),
    (0.005978712, 0.0001702080, 0.02827972),
    (0.006741117, 0.0001918160, 0.03189704),
    (0.007650000, 0.0002170000, 0.03621000),
    (0.008751373, 0.0002469067, 0.04143771),
    (0.01002888, 0.0002812400, 0.04750372),
    (0.01142170, 0.0003185200, 0.05411988),
    (0.01286901, 0.0003572667, 0.06099803),
    (0.01431000, 0.0003960000, 0.06785001),
    (0.01570443, 0.0004337147, 0.07448632),
    (0.01714744, 0.0004730240, 0.08136156),
    (0.01878122, 0.0005178760, 0.08915364),
    (0.02074801, 0.0005722187, 0.09854048),
    (0.02319000, 0.0006400000, 0.1102000),
    (0.02620736, 0.0007245600, 0.1246133),
    (0.02978248, 0.0008255000, 0.1417017),
    (0.03388092, 0.0009411600, 0.1613035),
    (0.03846824, 0.001069880, 0.1832568),
    (0.04351000, 0.001210000, 0.2074000),
    (0.04899560, 0.001362091, 0.2336921),
    (0.05502260, 0.001530752, 0.2626114),
    (0.06171880, 0.001720368, 0.2947746),
    (0.06921200, 0.001935323, 0.3307985),
    (0.07763000, 0.002180000, 0.3713000),
    (0.08695811, 0.002454800, 0.4162091),
    (0.09717672, 0.002764000, 0.4654642),
    (0.1084063, 0.003117800, 0.5196948),
    (0.1207672, 0.003526400, 0.5795303),
    (0.1343800, 0.004000000, 0.6456000),
    (0.1493582, 0.004546240, 0.7184838),
    (0.1653957, 0.005159320, 0.7967133),
    (0.1819831, 0.005829280, 0.8778459),
    (0.1986110, 0.006546160, 0.9594390),
    (0.2147700, 0.007300000, 1.0390501),
    (0.2301868, 0.008086507, 1.1153673),
    (0.2448797, 0.008908720, 1.1884971),
    (0.2587773, 0.009767680, 1.2581233),
    (0.2718079, 0.01066443, 1.3239296),
    (0.2839000, 0.01160000, 1.3856000),
    (0.2949438, 0.01257317, 1.4426352),
    (0.3048965, 0.01358272, 1.4948035),
    (0.3137873, 0.01462968, 1.5421903),
    (0.3216454, 0.01571509, 1.5848807),
    (0.3285000, 0.01684000, 1.6229600),
    (0.3343513, 0.01800736, 1.6564048),
    (0.3392101, 0.01921448, 1.6852959),
    (0.3431213, 0.02045392, 1.7098745),
    (0.3461296, 0.02171824, 1.7303821),
    (0.3482800, 0.02300000, 1.7470600),
    (0.3495999, 0.02429461, 1.7600446),
    (0.3501474, 0.02561024, 1.7696233),
    (0.3500130, 0.02695857, 1.7762637),
    (0.3492870, 0.02835125, 1.7804334),
    (0.3480600, 0.02980000, 1.7826000),
    (0.3463733, 0.03131083, 1.7829682),
    (0.3442624, 0.03288368, 1.7816998),
    (0.3418088, 0.03452112, 1.7791982),
    (0.3390941, 0.03622571, 1.7758671),
    (0.3362000, 0.03800000, 1.7721100),
    (0.3331977, 0.03984667, 1.7682589),
    (0.3300411, 0.04176800, 1.7640390),
    (0.3266357, 0.04376600, 1.7589438),
    (0.3228868, 0.04584267, 1.7524663),
    (0.3187000, 0.04800000, 1.7441000),
    (0.3140251, 0.05024368, 1.7335595),
    (0.3088840, 0.05257304, 1.7208581),
    (0.3032904, 0.05498056, 1.7059369),
    (0.2972579, 0.05745872, 1.6887372),
    (0.2908000, 0.06000000, 1.6692000),
    (0.2839701, 0.06260197, 1.6475287),
    (0.2767214, 0.06527752, 1.6234127),
    (0.2689178, 0.06804208, 1.5960223),
    (0.2604227, 0.07091109, 1.5645280),
    (0.2511000, 0.07390000, 1.5281000),
    (0.2408475, 0.07701600, 1.4861114),
    (0.2298512, 0.08026640, 1.4395215),
    (0.2184072, 0.08366680, 1.3898799),
    (0.2068115, 0.08723280, 1.3387362),
    (0.1953600, 0.09098000, 1.2876400),
    (0.1842136, 0.09491755, 1.2374223),
    (0.1733273, 0.09904584, 1.1878243),
    (0.1626881, 0.1033674, 1.1387611),
    (0.1522833, 0.1078846, 1.0901480),
    (0.1421000, 0.1126000, 1.0419000),
    (0.1321786, 0.1175320, 0.9941976),
    (0.1225696, 0.1226744, 0.9473473),
    (0.1132752, 0.1279928, 0.9014531),
    (0.1042979, 0.1334528, 0.8566193),
    (0.09564000, 0.1390200, 0.8129501),
    (0.08729955, 0.1446764, 0.7705173),
    (0.07930804, 0.1504693, 0.7294448),
    (0.07171776, 0.1564619, 0.6899136),
    (0.06458099, 0.1627177, 0.6521049),
    (0.05795001, 0.1693000, 0.6162000),
    (0.05186211, 0.1762431, 0.5823286),
    (0.04628152, 0.1835581, 0.5504162),
    (0.04115088, 0.1912735, 0.5203376),
    (0.03641283, 0.1994180, 0.4919673),
    (0.03201000, 0.2080200, 0.4651800),
    (0.02791720, 0.2171199, 0.4399246),
    (0.02414440, 0.2267345, 0.4161836),
    (0.02068700, 0.2368571, 0.3938822),
    (0.01754040, 0.2474812, 0.3729459),
    (0.01470000, 0.2586000, 0.3533000),
    (0.01216179, 0.2701849, 0.3348578),
    (0.009919960, 0.2822939, 0.3175521),
    (0.007967240, 0.2950505, 0.3013375),
    (0.006296346, 0.3085780, 0.2861686),
    (0.004900000, 0.3230000, 0.2720000),
    (0.003777173, 0.3384021, 0.2588171),
    (0.002945320, 0.3546858, 0.2464838),
    (0.002424880, 0.3716986, 0.2347718),
    (0.002236293, 0.3892875, 0.2234533),
    (0.002400000, 0.4073000, 0.2123000),
    (0.002925520, 0.4256299, 0.2011692),
    (0.003836560, 0.4443096, 0.1901196),
    (0.005174840, 0.4633944, 0.1792254),
    (0.006982080, 0.4829395, 0.1685608),
    (0.009300000, 0.5030000, 0.1582000),
    (0.01214949, 0.5235693, 0.1481383),
    (0.01553588, 0.5445120, 0.1383758),
    (0.01947752, 0.5656900, 0.1289942),
    (0.02399277, 0.5869653, 0.1200751),
    (0.02910000, 0.6082000, 0.1117000),
    (0.03481485, 0.6293456, 0.1039048),
    (0.04112016, 0.6503068, 0.09666748),
    (0.04798504, 0.6708752, 0.08998272),
    (0.05537861, 0.6908424, 0.08384531),
    (0.06327000, 0.7100000, 0.07824999),
    (0.07163501, 0.7281852, 0.07320899),
    (0.08046224, 0.7454636, 0.06867816),
    (0.08973996, 0.7619694, 0.06456784),
    (0.09945645, 0.7778368, 0.06078835),
    (0.1096000, 0.7932000, 0.05725001),
    (0.1201674, 0.8081104, 0.05390435),
    (0.1311145, 0.8224962, 0.05074664),
    (0.1423679, 0.8363068, 0.04775276),
    (0.1538542, 0.8494916, 0.04489859),
    (0.1655000, 0.8620000, 0.04216000),
    (0.1772571, 0.8738108, 0.03950728),
    (0.1891400, 0.8849624, 0.03693564),
    (0.2011694, 0.8954936, 0.03445836),
    (0.2133658, 0.9054432, 0.03208872),
    (0.2257499, 0.9148501, 0.02984000),
    (0.2383209, 0.9237348, 0.02771181),
    (0.2510668, 0.9320924, 0.02569444),
    (0.2639922, 0.9399226, 0.02378716),
    (0.2771017, 0.9472252, 0.02198925),
    (0.2904000, 0.9540000, 0.02030000),
    (0.3038912, 0.9602561, 0.01871805),
    (0.3175726, 0.9660074, 0.01724036),
    (0.3314384, 0.9712606, 0.01586364),
    (0.3454828, 0.9760225, 0.01458461),
    (0.3597000, 0.9803000, 0.01340000),
    (0.3740839, 0.9840924, 0.01230723),
    (0.3886396, 0.9874812, 0.01130188),
    (0.4033784, 0.9903128, 0.01037792),
    (0.4183115, 0.9928116, 0.009529306),
    (0.4334499, 0.9949501, 0.008749999),
    (0.4487953, 0.9967108, 0.008035200),
    (0.4643360, 0.9980983, 0.007381600),
    (0.4800640, 0.9991120, 0.006785400),
    (0.4959713, 0.9997482, 0.006242800),
    (0.5120501, 1.0000000, 0.005749999),
    (0.5282959, 0.9998567, 0.005303600),
    (0.5446916, 0.9993046, 0.004899800),
    (0.5612094, 0.9983255, 0.004534200),
    (0.5778215, 0.9968987, 0.004202400),
    (0.5945000, 0.9950000, 0.003900000),
    (0.6112209, 0.9926005, 0.003623200),
    (0.6279758, 0.9897426, 0.003370600),
    (0.6447602, 0.9864444, 0.003141400),
    (0.6615697, 0.9827241, 0.002934800),
    (0.6784000, 0.9786000, 0.002749999),
    (0.6952392, 0.9740837, 0.002585200),
    (0.7120586, 0.9691712, 0.002438600),
    (0.7288284, 0.9638568, 0.002309400),
    (0.7455188, 0.9581349, 0.002196800),
    (0.7621000, 0.9520000, 0.002100000),
    (0.7785432, 0.9454504, 0.002017733),
    (0.7948256, 0.9384992, 0.001948200),
    (0.8109264, 0.9311628, 0.001889800),
    (0.8268248, 0.9234576, 0.001840933),
    (0.8425000, 0.9154000, 0.001800000),
    (0.8579325, 0.9070064, 0.001766267),
    (0.8730816, 0.8982772, 0.001737800),
    (0.8878944, 0.8892048, 0.001711200),
    (0.9023181, 0.8797816, 0.001683067),
    (0.9163000, 0.8700000, 0.001650001),
    (0.9297995, 0.8598613, 0.001610133),
    (0.9427984, 0.8493920, 0.001564400),
    (0.9552776, 0.8386220, 0.001513600),
    (0.9672179, 0.8275813, 0.001458533),
    (0.9786000, 0.8163000, 0.001400000),
    (0.9893856, 0.8047947, 0.001336667),
    (0.9995488, 0.7930820, 0.001270000),
    (1.0090892, 0.7811920, 0.001205000),
    (1.0180064, 0.7691547, 0.001146667),
    (1.0263000, 0.7570000, 0.001100000),
    (1.0339827, 0.7447541, 0.001068800),
    (1.0409860, 0.7324224, 0.001049400),
    (1.0471880, 0.7200036, 0.001035600),
    (1.0524667, 0.7074965, 0.001021200),
    (1.0567000, 0.6949000, 0.001000000),
    (1.0597944, 0.6822192, 0.0009686400),
    (1.0617992, 0.6694716, 0.0009299200),
    (1.0628068, 0.6566744, 0.0008868800),
    (1.0629096, 0.6438448, 0.0008425600),
    (1.0622000, 0.6310000, 0.0008000000),
    (1.0607352, 0.6181555, 0.0007609600),
    (1.0584436, 0.6053144, 0.0007236800),
    (1.0552244, 0.5924756, 0.0006859200),
    (1.0509768, 0.5796379, 0.0006454400),
    (1.0456000, 0.5668000, 0.0006000000),
    (1.0390369, 0.5539611, 0.0005478667),
    (1.0313608, 0.5411372, 0.0004916000),
    (1.0226662, 0.5283528, 0.0004354000),
    (1.0130477, 0.5156323, 0.0003834667),
    (1.0026000, 0.5030000, 0.0003400000),
    (0.9913675, 0.4904688, 0.0003072533),
    (0.9793314, 0.4780304, 0.0002831600),
    (0.9664916, 0.4656776, 0.0002654400),
    (0.9528479, 0.4534032, 0.0002518133),
    (0.9384000, 0.4412000, 0.0002400000),
    (0.9231940, 0.4290800, 0.0002295467),
    (0.9072440, 0.4170360, 0.0002206400),
    (0.8905020, 0.4050320, 0.0002119600),
    (0.8729200, 0.3930320, 0.0002021867),
    (0.8544499, 0.3810000, 0.0001900000),
    (0.8350840, 0.3689184, 0.0001742133),
    (0.8149460, 0.3568272, 0.0001556400),
    (0.7941860, 0.3447768, 0.0001359600),
    (0.7729540, 0.3328176, 0.0001168533),
    (0.7514000, 0.3210000, 0.0001000000),
    (0.7295836, 0.3093381, 0.00008613333),
    (0.7075888, 0.2978504, 0.00007460000),
    (0.6856022, 0.2865936, 0.00006500000),
    (0.6638104, 0.2756245, 0.00005693333),
    (0.6424000, 0.2650000, 0.00004999999),
    (0.6215149, 0.2547632, 0.00004416000),
    (0.6011138, 0.2448896, 0.00003948000),
    (0.5811052, 0.2353344, 0.00003572000),
    (0.5613977, 0.2260528, 0.00003264000),
    (0.5419000, 0.2170000, 0.00003000000),
    (0.5225995, 0.2081616, 0.00002765333),
    (0.5035464, 0.1995488, 0.00002556000),
    (0.4847436, 0.1911552, 0.00002364000),
    (0.4661939, 0.1829744, 0.00002181333),
    (0.4479000, 0.1750000, 0.00002000000),
    (0.4298613, 0.1672235, 0.00001813333),
    (0.4120980, 0.1596464, 0.00001620000),
    (0.3946440, 0.1522776, 0.00001420000),
    (0.3775333, 0.1451259, 0.00001213333),
    (0.3608000, 0.1382000, 0.00001000000),
    (0.3444563, 0.1315003, 0.000007733333),
    (0.3285168, 0.1250248, 0.000005400000),
    (0.3130192, 0.1187792, 0.000003200000),
    (0.2980011, 0.1127691, 0.000001333333),
    (0.2835000, 0.1070000, 0.000000000000),
    (0.2695448, 0.1014762, 0.0),
    (0.2561184, 0.09618864, 0.0),
    (0.2431896, 0.09112296, 0.0),
    (0.2307272, 0.08626485, 0.0),
    (0.2187000, 0.08160000, 0.0),
    (0.2070971, 0.07712064, 0.0),
    (0.1959232, 0.07282552, 0.0),
    (0.1851708, 0.06871008, 0.0),
    (0.1748323, 0.06476976, 0.0),
    (0.1649000, 0.06100000, 0.0),
    (0.1553667, 0.05739621, 0.0),
    (0.1462300, 0.05395504, 0.0),
    (0.1374900, 0.05067376, 0.0),
    (0.1291467, 0.04754965, 0.0),
    (0.1212000, 0.04458000, 0.0),
    (0.1136397, 0.04175872, 0.0),
    (0.1064650, 0.03908496, 0.0),
    (0.09969044, 0.03656384, 0.0),
    (0.09333061, 0.03420048, 0.0),
    (0.08740000, 0.03200000, 0.0),
    (0.08190096, 0.02996261, 0.0),
    (0.07680428, 0.02807664, 0.0),
    (0.07207712, 0.02632936, 0.0),
    (0.06768664, 0.02470805, 0.0),
    (0.06360000, 0.02320000, 0.0),
    (0.05980685, 0.02180077, 0.0),
    (0.05628216, 0.02050112, 0.0),
    (0.05297104, 0.01928108, 0.0),
    (0.04981861, 0.01812069, 0.0),
    (0.04677000, 0.01700000, 0.0),
    (0.04378405, 0.01590379, 0.0),
    (0.04087536, 0.01483718, 0.0),
    (0.03807264, 0.01381068, 0.0),
    (0.03540461, 0.01283478, 0.0),
    (0.03290000, 0.01192000, 0.0),
    (0.03056419, 0.01106831, 0.0),
    (0.02838056, 0.01027339, 0.0),
    (0.02634484, 0.009533311, 0.0),
    (0.02445275, 0.008846157, 0.0),
    (0.02270000, 0.008210000, 0.0),
    (0.02108429, 0.007623781, 0.0),
    (0.01959988, 0.007085424, 0.0),
    (0.01823732, 0.006591476, 0.0),
    (0.01698717, 0.006138485, 0.0),
    (0.01584000, 0.005723000, 0.0),
    (0.01479064, 0.005343059, 0.0),
    (0.01383132, 0.004995796, 0.0),
    (0.01294868, 0.004676404, 0.0),
    (0.01212920, 0.004380075, 0.0),
    (0.01135916, 0.004102000, 0.0),
    (0.01062935, 0.003838453, 0.0),
    (0.009938846, 0.003589099, 0.0),
    (0.009288422, 0.003354219, 0.0),
    (0.008678854, 0.003134093, 0.0),
    (0.008110916, 0.002929000, 0.0),
    (0.007582388, 0.002738139, 0.0),
    (0.007088746, 0.002559876, 0.0),
    (0.006627313, 0.002393244, 0.0),
    (0.006195408, 0.002237275, 0.0),
    (0.005790346, 0.002091000, 0.0),
    (0.005409826, 0.001953587, 0.0),
    (0.005052583, 0.001824580, 0.0),
    (0.004717512, 0.001703580, 0.0),
    (0.004403507, 0.001590187, 0.0),
    (0.004109457, 0.001484000, 0.0),
    (0.003833913, 0.001384496, 0.0),
    (0.003575748, 0.001291268, 0.0),
    (0.003334342, 0.001204092, 0.0),
    (0.003109075, 0.001122744, 0.0),
    (0.002899327, 0.001047000, 0.0),
    (0.002704348, 0.0009765896, 0.0),
    (0.002523020, 0.0009111088, 0.0),
    (0.002354168, 0.0008501332, 0.0),
    (0.002196616, 0.0007932384, 0.0),
    (0.002049190, 0.0007400000, 0.0),
    (0.001910960, 0.0006900827, 0.0),
    (0.001781438, 0.0006433100, 0.0),
    (0.001660110, 0.0005994960, 0.0),
    (0.001546459, 0.0005584547, 0.0),
    (0.001439971, 0.0005200000, 0.0),
    (0.001340042, 0.0004839136, 0.0),
    (0.001246275, 0.0004500528, 0.0),
    (0.001158471, 0.0004183452, 0.0),
    (0.001076430, 0.0003887184, 0.0),
    (0.0009999493, 0.0003611000, 0.0),
    (0.0009287358, 0.0003353835, 0.0),
    (0.0008624332, 0.0003114404, 0.0),
    (0.0008007503, 0.0002891656, 0.0),
    (0.0007433960, 0.0002684539, 0.0),
    (0.0006900786, 0.0002492000, 0.0),
    (0.0006405156, 0.0002313019, 0.0),
    (0.0005945021, 0.0002146856, 0.0),
    (0.0005518646, 0.0001992884, 0.0),
    (0.0005124290, 0.0001850475, 0.0),
    (0.0004760213, 0.0001719000, 0.0),
    (0.0004424536, 0.0001597781, 0.0),
    (0.0004115117, 0.0001486044, 0.0),
    (0.0003829814, 0.0001383016, 0.0),
    (0.0003566491, 0.0001287925, 0.0),
    (0.0003323011, 0.0001200000, 0.0),
    (0.0003097586, 0.0001118595, 0.0),
    (0.0002888871, 0.0001043224, 0.0),
    (0.0002695394, 0.00009733560, 0.0),
    (0.0002515682, 0.00009084587, 0.0),
    (0.0002348261, 0.00008480000, 0.0),
    (0.0002191710, 0.00007914667, 0.0),
    (0.0002045258, 0.00007385800, 0.0),
    (0.0001908405, 0.00006891600, 0.0),
    (0.0001780654, 0.00006430267, 0.0),
    (0.0001661505, 0.00006000000, 0.0),
    (0.0001550236, 0.00005598187, 0.0),
    (0.0001446219, 0.00005222560, 0.0),
    (0.0001349098, 0.00004871840, 0.0),
    (0.0001258520, 0.00004544747, 0.0),
    (0.0001174130, 0.00004240000, 0.0),
    (0.0001095515, 0.00003956104, 0.0),
    (0.0001022245, 0.00003691512, 0.0),
    (0.00009539445, 0.00003444868, 0.0),
    (0.00008902390, 0.00003214816, 0.0),
    (0.00008307527, 0.00003000000, 0.0),
    (0.00007751269, 0.00002799125, 0.0),
    (0.00007231304, 0.00002611356, 0.0),
    (0.00006745778, 0.00002436024, 0.0),
    (0.00006292844, 0.00002272461, 0.0),
    (0.00005870652, 0.00002120000, 0.0),
    (0.00005477028, 0.00001977855, 0.0),
    (0.00005109918, 0.00001845285, 0.0),
    (0.00004767654, 0.00001721687, 0.0),
    (0.00004448567, 0.00001606459, 0.0),
    (0.00004150994, 0.00001499000, 0.0),
    (0.00003873324, 0.00001398728, 0.0),
    (0.00003614203, 0.00001305155, 0.0),
    (0.00003372352, 0.00001217818, 0.0),
    (0.00003146487, 0.00001136254, 0.0),
    (0.00002935326, 0.00001060000, 0.0),
    (0.00002737573, 0.000009885877, 0.0),
    (0.00002552433, 0.000009217304, 0.0),
    (0.00002379376, 0.000008592362, 0.0),
    (0.00002217870, 0.000008009133, 0.0),
    (0.00002067383, 0.000007465700, 0.0),
    (0.00001927226, 0.000006959567, 0.0),
    (0.00001796640, 0.000006487995, 0.0),
    (0.00001674991, 0.000006048699, 0.0),
    (0.00001561648, 0.000005639396, 0.0),
    (0.00001455977, 0.000005257800, 0.0),
    (0.00001357387, 0.000004901771, 0.0),
    (0.00001265436, 0.000004569720, 0.0),
    (0.00001179723, 0.000004260194, 0.0),
    (0.00001099844, 0.000003971739, 0.0),
    (0.00001025398, 0.000003702900, 0.0),
    (0.000009559646, 0.000003452163, 0.0),
    (0.000008912044, 0.000003218302, 0.0),
    (0.000008308358, 0.000003000300, 0.0),
    (0.000007745769, 0.000002797139, 0.0),
    (0.000007221456, 0.000002607800, 0.0),
    (0.000006732475, 0.000002431220, 0.0),
    (0.000006276423, 0.000002266531, 0.0),
    (0.000005851304, 0.000002113013, 0.0),
    (0.000005455118, 0.000001969943, 0.0),
    (0.000005085868, 0.000001836600, 0.0),
    (0.000004741466, 0.000001712230, 0.0),
    (0.000004420236, 0.000001596228, 0.0),
    (0.000004120783, 0.000001488090, 0.0),
    (0.000003841716, 0.000001387314, 0.0),
    (0.000003581652, 0.000001293400, 0.0),
    (0.000003339127, 0.000001205820, 0.0),
    (0.000003112949, 0.000001124143, 0.0),
    (0.000002902121, 0.000001048009, 0.0),
    (0.000002705645, 0.0000009770578, 0.0),
    (0.000002522525, 0.0000009109300, 0.0),
    (0.000002351726, 0.0000008492513, 0.0),
    (0.000002192415, 0.0000007917212, 0.0),
    (0.000002043902, 0.0000007380904, 0.0),
    (0.000001905497, 0.0000006881098, 0.0),
    (0.000001776509, 0.0000006415300, 0.0),
    (0.000001656215, 0.0000005980895, 0.0),
    (0.000001544022, 0.0000005575746, 0.0),
    (0.000001439440, 0.0000005198080, 0.0),
    (0.000001341977, 0.0000004846123, 0.0),
    (0.000001251141, 0.0000004518100, 0.0),
)  # }}}1

# CIE standard observer wavelengths range (nanometers)
CIE_STD_OBSERVER_RANGE = range(360, 831, 1)

# CIE standard observer
# (a dictionary between wavelengths and tristimuli)
CIE_STD_OBSERVER = dict(zip(CIE_STD_OBSERVER_RANGE, CIE_STD_OBSERVER_VALUES))

# Maximal photopic luminous efficacy (lm/W)
MAX_PHOTOPIC_LUMINOUS_EFFICACY = 683.0  # Luminous efficiency=100%


# ===========================================================================
#                               Atmosphere data
# ===========================================================================


# Spectral Power Distributions (SPD) for atmosphere components
# (wavelength (nm), amplitude)

# Ozone spectral power distribution (sampled)
OZONE_SAMPLED_SPD = (  # {{{1
    (300, 10.0),
    (305, 4.8),
    (310, 2.7),
    (315, 1.35),
    (320, 0.8),
    (325, 0.380),
    (330, 0.160),
    (335, 0.075),
    (340, 0.04),
    (345, 0.019),
    (350, 0.007),
    (355, 0.0),
    (445, 0.003),
    (450, 0.003),
    (455, 0.004),
    (460, 0.006),
    (465, 0.008),
    (470, 0.009),
    (475, 0.012),
    (480, 0.014),
    (485, 0.017),
    (490, 0.021),
    (495, 0.025),
    (500, 0.03),
    (505, 0.035),
    (510, 0.04),
    (515, 0.045),
    (520, 0.048),
    (525, 0.057),
    (530, 0.063),
    (535, 0.07),
    (540, 0.075),
    (545, 0.08),
    (550, 0.085),
    (555, 0.095),
    (560, 0.103),
    (565, 0.110),
    (570, 0.12),
    (575, 0.122),
    (580, 0.12),
    (585, 0.118),
    (590, 0.115),
    (595, 0.12),
    (600, 0.125),
    (605, 0.130),
    (610, 0.12),
    (620, 0.105),
    (630, 0.09),
    (640, 0.079),
    (650, 0.067),
    (660, 0.057),
    (670, 0.048),
    (680, 0.036),
    (690, 0.028),
    (700, 0.023),
    (710, 0.018),
    (720, 0.014),
    (730, 0.011),
    (740, 0.010),
    (750, 0.009),
    (760, 0.007),
    (770, 0.004),
    (780, 0.0),
    (790, 0.0),
)  # }}}1

# Mixed gases spectral power distribution (sampled)
GASES_SAMPLED_SPD = (  # {{{1
    (759.0, 0.00),
    (760.0, 3.00),
    (770.0, 0.21),
    (771.0, 0.00),
)  # }}}1

# Water spectral power distribution (sampled)
WATER_SAMPLED_SPD = (  # {{{1
    (689.0, 0.000e0),
    (690.0, 0.160e-1),
    (700.0, 0.240e-1),
    (710.0, 0.125e-1),
    (720.0, 0.100e1),
    (730.0, 0.870e0),
    (740.0, 0.610e-1),
    (750.0, 0.100e-2),
    (760.0, 0.100e-4),
    (770.0, 0.100e-4),
    (780.0, 0.600e-3),
    (790.0, 0.175e-1),
    (800.0, 0.360e-1),
)  # }}}1

# Amount of atmospheric absorbing components, in centimeters NTP
# Nota: centimeters NTP refers to the height in centimeters at normal
# temperature and pressure (NTP) of a unit-area column containing mass
# equivalent to the integrated mass of absorbing material.
# NTP are taken to be 0°C and 1013 hpa.

# Amount of ozone in atmosphere
OZONE_AMOUNT = 0.35  # Centimeters NTP

# Amount of precipitable water vapor in atmosphere
WATER_AMOUNT = 2.0  # Centimeters NTP


# ===========================================================================
#                               Sun data
# ===========================================================================

# Sun spectral radiance distribution (sampled),
# in range 380-750 nm by 10nm in W.m-2.sr-1.nm-1
SUN_SAMPLED_SRD = (  # {{{1
    (380, 16559.0),
    (390, 16233.7),
    (400, 21127.5),
    (410, 25888.2),
    (420, 25829.1),
    (430, 24232.3),
    (440, 26760.5),
    (450, 29658.3),
    (460, 30545.4),
    (470, 30057.5),
    (480, 30663.7),
    (490, 28830.4),
    (500, 28712.1),
    (510, 27825.0),
    (520, 27100.6),
    (530, 27233.6),
    (540, 26361.3),
    (550, 25503.8),
    (560, 25060.2),
    (570, 25311.6),
    (580, 25355.9),
    (590, 25134.2),
    (600, 24631.5),
    (610, 24173.2),
    (620, 23685.3),
    (630, 23212.1),
    (640, 22827.7),
    (650, 22339.8),
    (660, 21970.2),
    (670, 21526.7),
    (680, 21097.9),
    (690, 20728.3),
    (700, 20240.4),
    (710, 19870.8),
    (720, 19427.2),
    (730, 19072.4),
    (740, 18628.9),
    (750, 18259.2),
)  # }}}1

SUN_RADIUS = 695500000.0  # Meters
SUN_MEAN_DISTANCE = 149600000000.0  # Meters


# ===========================================================================
#                           Interpolation feature
# ===========================================================================


class Interpolation:
    """Linear interpolation helper class for sampled (x,y) data.

    Interpolation is called via [] operator.
    Note: no extrapolation is done.
    """

    def __init__(self, samples):
        """Initialize interpolation with sampled data.

        Args:
            samples -- an iterable on (x,y) data.
        """
        samples = tuple(samples)
        assert len(samples) > 1, "Samples must contains at least 2 elements"
        x_list = self.x_list = [float(x[0]) for x in samples]
        y_list = self.y_list = [float(x[1]) for x in samples]
        assert all(
            y - x > 0 for x, y in zip(x_list, x_list[1:])
        ), "x list must be in strictly ascending order!"
        intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
        self.slopes = [(y2 - y1) / (x2 - x1) for x1, x2, y1, y2 in intervals]

    def __repr__(self):
        """Get a representation of the object."""
        res = [f"({x}, {y})" for x, y in zip(self.x_list, self.y_list)]
        res = ", ".join(res)
        return f"Interpolate({res})"

    def __getitem__(self, parameter):
        """Return an interpolated value.

        Nota:
            - No extrapolation.
            - If parameter is out of sample range, return 0.
        """
        param = float(parameter)
        x_list, y_list, slopes = self.x_list, self.y_list, self.slopes

        # Out of range?
        if not x_list[0] <= param <= x_list[-1]:
            return 0.0

        index = bisect.bisect_left(self.x_list, param) - 1
        return (
            y_list[0]
            if index == -1
            else y_list[index] + slopes[index] * (param - x_list[index])
        )


# ===========================================================================
#                           XYZ Color handler
# ===========================================================================


class ColorXYZ:
    """Color in CIE XYZ color system."""

    # pylint: disable=invalid-name
    def __init__(self, p_X=0, p_Y=0, p_Z=0):
        """Initialize ColorXYZ.

        Args:
            p_X -- X component (float)
            p_Y -- Y component (float)
            p_Z -- Z component (float)
        """
        self.X = float(p_X)
        self.Y = float(p_Y)
        self.Z = float(p_Z)

    @classmethod
    def from_xyY(cls, x, y, Y):
        """Initialize a ColorXYZ from a xyY color."""
        return cls(Y / y * x, Y, Y / y * (1 - x - y))

    @classmethod
    def from_srd(cls, srd):
        """Initialize a ColorXYZ from a spectral radiance distribution.

        This method operates conversion from radiometry (spectral radiance)
        to photometry (XYZ color)
        It uses CIE 1931 2° standard observer. Resulting Y is absolute
        luminance (normalization).

        Args:
            srd -- spectral radiance distribution
                This has to be a dict of radiometric radiances in
                W.sr-1.m-2, indexed by wavelengths in nm, for all
                wavelengths in CIE_STD_OBSERVER_RANGE (360->830)

        Returns:
            CIE XYZ color, with Y being absolute luminance (in cd.m-2)
        """
        # Integration of SRD
        # w -- wavelength
        # d_w -- differential of wavelength
        # rng -- range of integration
        rng = CIE_STD_OBSERVER_RANGE
        d_w = float(rng.step)
        color = sum(cls(*CIE_STD_OBSERVER[w]) * srd[w] * d_w for w in rng)

        # Normalization
        color *= MAX_PHOTOPIC_LUMINOUS_EFFICACY

        return color

    def __add__(self, other):
        """Compute addition between this object and another one."""
        # For sum function (starts with 0...)
        if isinstance(other, numbers.Integral) and other == 0:
            return self

        try:
            return ColorXYZ(
                self.X + other.X, self.Y + other.Y, self.Z + other.Z
            )
        except (AttributeError, TypeError):
            return NotImplemented

    def __radd__(self, other):
        """Compute reverse addition between this object and another one."""
        return self + other

    def __iadd__(self, other):
        """Compute in-place addition between this object and another one."""
        try:
            self.X += float(other.X)
            self.Y += float(other.Y)
            self.Z += float(other.Z)
            return self
        except (TypeError, ValueError):
            return NotImplemented

    def __mul__(self, scalar):
        """Compute multiplication between this object and a scalar."""
        try:
            _scalar = float(scalar)
            return ColorXYZ(
                self.X * _scalar, self.Y * _scalar, self.Z * _scalar
            )
        except (TypeError, ValueError):
            return NotImplemented

    def __rmul__(self, scalar):
        """Compute reverse multiplication between this object and a scalar."""
        return self * scalar

    def __repr__(self):
        """Return a representation of this object."""
        fmtstr = "XYZ({:3.2e}, {:3.2e}, {:3.2e})"
        return fmtstr.format(self.X, self.Y, self.Z)

    def to_xyY(self):
        """Convert this color to xyY color."""
        tot = self.X + self.Y + self.Z
        res = namedtuple("xyY", ["x", "y", "Y"])(
            self.X / tot, self.Y / tot, self.Y
        )
        return res

    def to_srgb(self):
        """Convert this color to sRGB color."""
        # From wikipedia
        # https://en.wikipedia.org/wiki/SRGB#The_forward_transformation_(CIE_XYZ_to_sRGB)

        def gamma_compress(u):
            """Compute gamma compression for a RGB component.

            Args:
                u -- The RGB component.
            """
            return (
                12.92 * u if u <= 0.0031308 else 1.055 * u ** (5 / 12) - 0.055
            )

        # Linear transformation
        red = +3.24096994 * self.X - 1.53738318 * self.Y - 0.49861076 * self.Z
        grn = -0.96924364 * self.X + 1.87596750 * self.Y + 0.04155506 * self.Z
        blu = +0.05563008 * self.X - 0.20397696 * self.Y + 1.05697151 * self.Z

        # Gamma compression
        red = gamma_compress(red)
        grn = gamma_compress(grn)
        blu = gamma_compress(blu)

        return namedtuple("srgb", ["r", "g", "b"])(red, grn, blu)

    def to_srgb_with_fixed_luminance(self, p_Y):
        """Convert this color to sRGB, at given Y (luminance) value."""
        xyy = self.to_xyY()
        xyz = ColorXYZ.from_xyY(xyy.x, xyy.y, p_Y)
        return xyz.to_srgb()


# ===========================================================================
#                               Sun light function
# ===========================================================================


# SPD/SRD interpolations, from sampled data
OZONE_SPD = Interpolation(OZONE_SAMPLED_SPD)
GASES_SPD = Interpolation(GASES_SAMPLED_SPD)
WATER_SPD = Interpolation(WATER_SAMPLED_SPD)
SUN_SRD = Interpolation(SUN_SAMPLED_SRD)


def sunlight(theta, turbidity):
    """Compute sun color in CIE XYZ.

    Takes into account atmosperich effects: Rayleigh scattering,
    aerosols (water+dust) absorption, ozone absorption, mixed gases absorption
    and water absorption
    Computed luminance (Y) is absolute luminance

    Args:
        theta -- Sun polar angle (in radians)
        turbidity -- Sky turbidity

    Returns:
        A (named) tuple providing:
        - solar irradiance (W.m-2)
        - solar illuminance (lm.m-2, or lux)
        - solar XYZ color (cd.m-2)
        as perceived from Earth, after atmospheric attenuation, for the given
        parameters.

    You may want to convert XYZ color to RGB via 'to_srgb' or
    'to_srgb_with_fixed_luminance' ColorXYZ methods.
    """

    def atm_transmittance(wavelength, mass, turbidity):
        """Compute atmosphere transmittance affected by atmospheric effects.

        The computation is done for a given wavelength, a given atmospheric
        optical mass and a given atmospheric turbidity.

        The atmospheric effects taken into account are:
        Rayleigh, aerosols, ozone, mixed gases, water

        Args:
            wavelength -- wavelength (nm)
            mass -- optical mass of the atmosphere involved in attenuation
            turbidity -- atmospheric turbidity

        Returns:
            Atmospheric transmittance.
        """
        # Rayleigh scattering
        # (for this, wavelength should be in micrometers)
        tau_r = exp(-mass * 0.008735 * (wavelength / 1000.0) ** -4.08)

        # Aerosols (water+dust) attenuation
        # (for this, wavelength should be in micrometers)
        # alpha -- ratio of small to large particle sizes. (0:4, usually 1.3)
        # beta -- amount of aerosols present
        alpha = 1.3
        beta = 0.04608365822050 * turbidity - 0.04586025928522
        tau_a = exp(-mass * beta * (wavelength / 1000.0) ** -alpha)

        # Ozone absorption attenuation
        tau_o = exp(-mass * OZONE_SPD[wavelength] * OZONE_AMOUNT)

        # Mixed gases absorption attenuation
        tau_g = exp(
            -1.41
            * GASES_SPD[wavelength]
            * mass
            / (1.0 + 118.93 * GASES_SPD[wavelength] * mass) ** 0.45
        )

        # Water absorption attenuation
        tau_wa = exp(
            -0.2385
            * WATER_SPD[wavelength]
            * WATER_AMOUNT
            * mass
            / (1.0 + 20.07 * WATER_SPD[wavelength] * WATER_AMOUNT * mass)
            ** 0.45
        )

        return tau_r * tau_a * tau_o * tau_g * tau_wa

    # 'sunlight' function starts here

    # Relative atmosphere Optical Mass
    mass = 1.0 / (cos(theta) + 0.00094 * (1.6386 - theta) ** -1.253)

    # Sun atmospheric-attenuated spectral radiance distribution (srd)
    # from Sun extraterrestrial srd and atmospheric transmittance
    # w -- wavelength (nm)
    attenuated_srd = Interpolation(
        (w, SUN_SRD[w] * atm_transmittance(w, mass, turbidity))
        for w in range(350, 805, 5)
    )

    # Solid angle for a 1 m2 area on Earth, normal to sun rays,
    # being seen from the Sun
    solid_angle_1m2 = 1.0 * SUN_MEAN_DISTANCE**-2  # sr.m-2

    # Radiometric quantities
    radiance = sum(attenuated_srd[w] for w in range(380, 760))  # W.sr-1.m-2
    radiant_intensity = PI * SUN_RADIUS**2 * radiance  # W.sr-1
    irradiance = radiant_intensity * solid_angle_1m2  # W.m-2

    # Photometric quantities
    xyz = ColorXYZ.from_srd(attenuated_srd)
    luminance = xyz.Y  # cd.m-2
    luminous_intensity = PI * SUN_RADIUS**2 * luminance  # cd
    illuminance = luminous_intensity * solid_angle_1m2  # lux (cd.sr.m-2)

    res = namedtuple("Sunlight", ["irradiance", "illuminance", "xyz"])(
        irradiance=irradiance, illuminance=illuminance, xyz=xyz
    )

    return res


def _test():
    """Test 'sunlight' function (for debug use)."""

    def subtest(name, theta, turbidity):
        theta_r = radians(theta)
        sun = sunlight(theta_r, turbidity)
        # pylint: disable=consider-using-f-string
        print(
            ">>>>> {:9s} {} RGB({:3.2} {:3.2} {:3.2}) {:3.2e} W".format(
                name + ":",
                sun.xyz,
                *sun.xyz.to_srgb_with_fixed_luminance(1.0),
                sun.irradiance,
            )
        )

    # For luminance (Y) orders of magnitude, see here:
    # https://en.wikipedia.org/wiki/Orders_of_magnitude_(luminance)
    turbidity = 2
    subtest("Zenith", 0, turbidity)
    subtest("45°", 45, turbidity)
    subtest("86°", 86, turbidity)
    subtest("Horizon", 90, turbidity)


# vim: foldmethod=marker
